Screw.Matchers["be_the_same_as"] = {
	match: function(expected, actual) {
		if(expected instanceof Array) {
			if(expected.length !== actual.length) return false;
			for(var i = 0; i < expected.length; i += 1) {
				if(expected[i] !== actual[i]) return false;
			}
			return true;
		} else {
			return expected === actual;
		}
	},
	failure_message: function(expected, actual, not) {
		return 'expected ' + $.print(actual) + (not ? ' to not equal ' : ' to equal ') + $.print(expected);
	}
};

(function($) {
Screw.Unit(function() {
	describe('A Tree', function() {
		var aTree;

		describe('with the default name of "items" for children', function() {
			before(function() {
				 aTree = tree();
			});

			describe('and with only leaf nodes as children', function() {
				var anItem = {},
						aSecondItem = {},
						aThirdItem = {};

				before(function() {
					aTree.append(anItem);
					aTree.append(aSecondItem);
					aTree.append(aThirdItem);
				});

				it('should be able to move an item up in the current nesting level', function() {
					aTree.move(aThirdItem).up();
					expect(aTree.items).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
				});

				it('should be able to move an item down in the current nesting level', function(){
					aTree.move(aSecondItem).down();
					expect(aTree.items).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
				});

				it('should be able to move an item down and then back up in current nesting level', function(){
					aTree.move(aSecondItem).down();
					aTree.move(aSecondItem).up();
					expect(aTree.items).to(be_the_same_as, [anItem, aSecondItem, aThirdItem]);
				});

				it('should be able to replace one item with another at the current nesting level', function() {
					var aFourthItem = {};
					
					aTree.replace(aSecondItem).withItem(aFourthItem);
					expect(aTree.items).to(be_the_same_as, [anItem, aFourthItem, aThirdItem]);
				});

				it('should be able to insert an item before another specified item at the current nesting level', function() {
					var aFourthItem = {};

					aTree.insert(aFourthItem).before(aSecondItem);
					expect(aTree.items).to(be_the_same_as, [anItem, aFourthItem, aSecondItem, aThirdItem]);
				});

				it('should be able to insert an item after another specified item at the current nesting level', function() {
					var aFourthItem = {};

					aTree.insert(aFourthItem).after(aSecondItem);
					expect(aTree.items).to(be_the_same_as, [anItem, aSecondItem, aFourthItem, aThirdItem]);
				});

				it('should be able to insert two items at the current nesting level', function() {
					var aFourthItem = {},
							aFifthItem = {};

					aTree.insert(aFourthItem).after(aSecondItem);
					aTree.insert(aFifthItem).after(aSecondItem);
					expect(aTree.items).to(be_the_same_as, [anItem, aSecondItem, aFifthItem, aFourthItem, aThirdItem]);
				});

				it('should be able to remove an item from the current nesting level by reference', function() {
					aTree.remove(aThirdItem);
					expect(aTree.items).to(be_the_same_as, [anItem, aSecondItem]);
				});

				it('should be able to remove an item from the current nesting level by predicate function', function() {
					aTree.remove(function(anItem) {
						return anItem === aSecondItem;
					});
					expect(aTree.items).to(be_the_same_as, [anItem, aThirdItem]);
				});
			});

			describe('and with a composite node as a child', function(){
				var aCompositeItem;

				describe('and the composite node has all leaf nodes as children', function() {
					var anItem = {},
							aSecondItem = {},
							aThirdItem = {};

					before(function() {
						aCompositeItem = {
							items: extendArray([ anItem, aSecondItem, aThirdItem ])
						};
						aTree.append(aCompositeItem);
					});

					it('should be able to move an item up in the composite', function(){
						aTree.move(aSecondItem).up();
						expect(aCompositeItem.items).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down in the composite', function(){
						aTree.move(anItem).down();
						expect(aCompositeItem.items).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down and then back up in the composite', function(){
						aTree.move(aSecondItem).down();
						aTree.move(aSecondItem).up();
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aThirdItem]);
					});

					it('should be able to replace one item with another in the composite', function() {
						var aFourthItem = {};

						aTree.replace(aSecondItem).withItem(aFourthItem);
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aFourthItem, aThirdItem]);
					});

					it('should be able to insert an item before another specified item in the composite', function() {
						var aFourthItem = {};
						aTree.insert(aFourthItem).before(aSecondItem);
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aFourthItem, aSecondItem, aThirdItem]);
					});

					it('should be able to insert an item after another specified item in the composite', function() {
						var aFourthItem = {};
						aTree.insert(aFourthItem).after(aSecondItem);
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aFourthItem, aThirdItem]);
					});

					it('should be able to insert two items into the composite', function() {
						var aFourthItem = {},
								aFifthItem = {};

						aTree.insert(aFourthItem).after(aSecondItem);
						aTree.insert(aFifthItem).after(aSecondItem);
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aFifthItem, aFourthItem, aThirdItem]);
					});

					it('should be able to remove an item from the composite by reference', function() {
						aTree.remove(aThirdItem);
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem]);
					});

					it('should be able to remove an item from the composite by predicate function', function() {
						aTree.remove(function(anItem) {
							return anItem === aThirdItem;
						});
						expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem]);
					});

					describe('and with a leaf nodes surrounding the composite node', function() {
						var aFourthItem = {},
								aFifthItem = {};

						before(function() {
							aTree.append(aFourthItem);
							aTree.insert(aFifthItem).before(aCompositeItem);
						});

						it('should move an item into a composite if the previous item in the tree is a composite', function() {
							aTree.move(aFourthItem).intoCompositeIfPreviousItemIsComposite();
							expect(aTree.items).to(be_the_same_as, [aFifthItem, aCompositeItem]);
							expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aThirdItem, aFourthItem]);
						});

						it('should not move an item if the previous item in the tree is not a composite', function() {
							aTree.move(aFifthItem).intoCompositeIfPreviousItemIsComposite();
							expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aThirdItem]);
						});

						it("should move an item out of a composite if the composite's parent is a composite", function() {
							aTree.move(aThirdItem).outOfCompositeIfParentIsComposite();
							expect(aTree.items).to(be_the_same_as, [aFifthItem, aCompositeItem, aThirdItem, aFourthItem]);
							expect(aCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem]);
						});

						it('should not move an item out of the first level of the tree', function() {
							aTree.move(aCompositeItem).outOfCompositeIfParentIsComposite();
							expect(aTree.items).to(be_the_same_as, [aFifthItem, aCompositeItem, aFourthItem]);
						});
					});
				});

				describe('and the composite node has a composite node as a child', function() {
					var aSecondCompositeItem,
							anItem = {},
							aSecondItem = {},
							aThirdItem = {};

					before(function() {
						aSecondCompositeItem = {
							items: extendArray([ anItem, aSecondItem, aThirdItem ])
						};
						aCompositeItem = {
							items: extendArray([ aSecondCompositeItem ])
						};
						aTree.append(aCompositeItem);
					});

					it('should be able to move an item up in the composite', function(){
						aTree.move(aSecondItem).up();
						expect(aSecondCompositeItem.items).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down in the composite', function(){
						aTree.move(anItem).down();
						expect(aSecondCompositeItem.items).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down and then back up in the composite', function(){
						aTree.move(aSecondItem).down();
						aTree.move(aSecondItem).up();
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aThirdItem]);
					});

					it('should be able to replace one item with another in the composite', function() {
						var aFourthItem = {};

						aTree.replace(aSecondItem).withItem(aFourthItem);
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aFourthItem, aThirdItem]);
					});

					it('should be able to insert an item before another specified item in the composite', function() {
						var aFourthItem = {};
						aTree.insert(aFourthItem).before(aSecondItem);
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aFourthItem, aSecondItem, aThirdItem]);
					});

					it('should be able to insert an item after another specified item in the composite', function() {
						var aFourthItem = {};
						aTree.insert(aFourthItem).after(aSecondItem);
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem, aFourthItem, aThirdItem]);
					});

					it('should be able to remove an item from the composite by reference', function() {
						aTree.remove(aThirdItem);
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem]);
					});

					it('should be able to remove an item from the composite by predicate function', function() {
						aTree.remove(function(anItem) {
							return anItem === aThirdItem;
						});
						expect(aSecondCompositeItem.items).to(be_the_same_as, [anItem, aSecondItem]);
					});
				});
			});
		});

		describe('with an alternate name of "children" for child nodes', function() {
			before(function() {
				aTree = tree({ childName: 'children' });
			});

			describe('and with only leaf nodes as children', function() {
				var anItem = {},
						aSecondItem = {},
						aThirdItem = {};

				before(function() {
					aTree.append(anItem);
					aTree.append(aSecondItem);
					aTree.append(aThirdItem);
				});

				it('should be able to move an item up in the current nesting level', function() {
					aTree.move(aThirdItem).up();
					expect(aTree.children).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
				});

				it('should be able to move an item down in the current nesting level', function(){
					aTree.move(aSecondItem).down();
					expect(aTree.children).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
				});
			});

			describe('and with a composite node as a child', function(){
				var aCompositeItem;

				describe('and the composite node has all leaf nodes as children', function() {
					var anItem = {},
							aSecondItem = {},
							aThirdItem = {};

					before(function() {
						aCompositeItem = {
							children: extendArray([ anItem, aSecondItem, aThirdItem ])
						};
						aTree.append(aCompositeItem);
					});

					it('should be able to move an item up in the composite', function(){
						aTree.move(aSecondItem).up();
						expect(aCompositeItem.children).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down in the composite', function(){
						aTree.move(anItem).down();
						expect(aCompositeItem.children).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});
				});

				describe('and the composite node has a composite node as a child', function() {
					var aSecondCompositeItem,
							anItem = {},
							aSecondItem = {},
							aThirdItem = {};

					before(function() {
						aSecondCompositeItem = {
							children: extendArray([ anItem, aSecondItem, aThirdItem ])
						};
						aCompositeItem = {
							children: extendArray([ aSecondCompositeItem ])
						};
						aTree.append(aCompositeItem);
					});

					it('should be able to move an item up in the composite', function(){
						aTree.move(aSecondItem).up();
						expect(aSecondCompositeItem.children).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});

					it('should be able to move an item down in the composite', function(){
						aTree.move(anItem).down();
						expect(aSecondCompositeItem.children).to(be_the_same_as, [ aSecondItem, anItem, aThirdItem ]);
					});
				});
			});
		});
	});
});
})(jQuery);

(function($) {
Screw.Unit(function() {
	describe('An enhanced Array', function() {
		var anEnhancedArray;

		before(function() {
			 anEnhancedArray = extendArray([]);
		});

		describe('containing one item', function() {
			var anItem = {};

			before(function() {
				anEnhancedArray.push(anItem);
			});

			it('should contain the item', function() {
				expect(anEnhancedArray.contains(anItem)).to(be_true);
			});

			it('should not contain a new item', function() {
				expect(anEnhancedArray.contains({})).to(be_false);
			});

			it('should remove the item', function() {
				expect(anEnhancedArray.remove(anItem)).to(be_the_same_as, []);
			});
		});

		describe('containing multiple items', function() {
			var anItem = {},
					aSecondItem = {},
					aThirdItem = {};

			before(function() {
				anEnhancedArray.push(anItem);
				anEnhancedArray.push(aSecondItem);
				anEnhancedArray.push(aThirdItem);
			});

			it('should return the proper index of an item', function() {
				expect(anEnhancedArray.indexOf(aSecondItem)).to(equal, 1);
			});

			it('should return an index of -1 if the item is not in the array', function(){
				expect(anEnhancedArray.indexOf({})).to(equal, -1);
			});

			it('should not move an item before previous if there is no previous item', function() {
				expect(anEnhancedArray.move(anItem).toBeforeThePreviousItem()).to(be_the_same_as, [anItem, aSecondItem, aThirdItem]);
			});

			it('should be able to move an item to before the previous item', function() {
				expect(anEnhancedArray.move(aThirdItem).toBeforeThePreviousItem()).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
			});

			it('should be able to move an item to after the next item', function() {
				expect(anEnhancedArray.move(aSecondItem).toAfterTheNextItem()).to(be_the_same_as, [anItem, aThirdItem, aSecondItem]);
			});

			it('should be able to replace one item with another', function() {
				var aFourthItem = {};
				expect(anEnhancedArray.replace(aSecondItem).withItem(aFourthItem)).to(be_the_same_as, [anItem, aFourthItem, aThirdItem]);
			});

			it('should be able to insert an item before an exisitng item', function() {
				var aFourthItem = {};
				expect(anEnhancedArray.insert(aFourthItem).before(aSecondItem)).to(be_the_same_as, [anItem, aFourthItem, aSecondItem, aThirdItem]);
			});

			it('should be able to insert an item after an exisitng item', function() {
				var aFourthItem = {};
				expect(anEnhancedArray.insert(aFourthItem).after(aSecondItem)).to(be_the_same_as, [anItem, aSecondItem, aFourthItem, aThirdItem]);
			});

			it('should return the item previous to an item if an item is not the first item in the array', function() {
				expect(anEnhancedArray.findItemPreviousTo(aSecondItem)).to(be_the_same_as, anItem);
			});

			it('should return undefined if the item is the first item in the array', function() {
				expect(anEnhancedArray.findItemPreviousTo(anItem)).to(be_undefined);
			});
		});
	});
});
})(jQuery);